#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <stdlib.h>
#include "buffer_manager.h"
#include "server_listener.h"

#define LOGIN_SUCCESS 	1
#define USR_FULL 	2
#define LOGIN_FAIL 	3
#define oops(msg)	{printf("server: "); perror(msg); exit(1);}

int	add_new_usr(char *, int);

int main(int ac, char *av[])
{
	struct sockaddr_in	saddr;
	int 		host_sock;
	int 		usr_name_len;
	int		usr_id;			//user info's position in database
	int 		usr_sock;
	char		usr_name[MAX_USR_ID_LEN];
	char		login_msg[MAX_USR_ID_LEN + 20];
	char		refuse_msg[] = "connection refused by server\n";
	pthread_t	distributor_thread;
	pthread_attr_t	attr_detached;
	
	//socket init
	if ((host_sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
		oops("socket");
	bzero((void *)&saddr, sizeof(saddr));
	saddr.sin_addr.s_addr = INADDR_ANY;
	saddr.sin_port = htons(atoi(av[1]));
	saddr.sin_family = AF_INET;
	if (bind(host_sock, (struct sockaddr *)&saddr, sizeof(saddr)) != 0)
		oops("bind");
	if (listen(host_sock, 1) != 0)
		oops("listen");

	//init variable
	for (int i = 0; i < MAX_USR_NUM; i++)
		usr[i].active = false;
	usr_num = 0;
	
	//start the distributor thread
	pthread_attr_init(&attr_detached);	//create detached threads
	pthread_attr_setdetachstate(&attr_detached, PTHREAD_CREATE_DETACHED);
	pthread_create(&distributor_thread, &attr_detached, distributor, NULL);
	
	printf("> ready!\n");

	//main loop: handle client calls
	while (1)
	{
		printf("> waiting...\n");
		if ((usr_sock = accept( host_sock, NULL, NULL)) == -1)
			oops("accept");
		printf("> new user knocking...\n");
		//user login: input user id 
		if ((usr_name_len = read(usr_sock, usr_name, MAX_USR_ID_LEN)) <= 0)
		{
			printf("> user name lenth is invalid. connection closed. \n");
			close(usr_sock);
			continue;
		}
		if (usr_name_len > 2 && usr_name[usr_name_len - 2] == '\r')	//java string: \r\n
		    usr_name[usr_name_len - 2] = '\0';
		else					//c string:	\n
		    usr_name[usr_name_len] = '\0';
		
		if ( (usr_id = add_new_usr(usr_name, usr_sock)) == -1)
		{
			close(usr_sock);
			continue;
		}
		//creat child thread
		pthread_create(&usr[usr_id].thread_id, &attr_detached, server_listener, (void *)&usr_id);
		printf("> user:[ %s] login success!\n", usr_name);
		printf("> current online user number: %d\n", usr_num);
		strcpy(login_msg, usr_name);
		strcat(login_msg, " login!\n");
		add_to_buffer(login_msg);
	}

	pthread_attr_destroy(&attr_detached);
}

int add_new_usr(char *usr_name, int usr_sock)
{
	int ret = -1;

	pthread_mutex_lock(&usr_lock);
	if (usr_num < MAX_USR_NUM)
	{	
		for (int i = 0 ; i < MAX_USR_NUM; i++)
		{
			if (usr[i].active == false)
			{
				usr[i].active = true;
				strcpy(usr[i].name, usr_name);
				usr[i].sock_id = usr_sock;
				sock_list.push_back(usr_sock);
				ret = i;
				break;
			}
		}
	}
	usr_num++;

	pthread_mutex_unlock(&usr_lock);

	return ret;
}
